%{

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>

    #include "global.h"
    #include "code4adresses.h"
    #include "tableHashage.h"

    #define YYERROR_VERBOSE 1

    #define SIZE_TAB1 384
    #define SIZE_TAB2 32

    #define VARIABLE 0
    #define FONCTION 1

    #define CONSTANTE 0
    #define ADDR_MEM  1

    #define _INT 0
    #define _ID  1

    #define TRUE  0
    #define FALSE 1


    // variables utilisées pour la gestion de la table des symboles
    listeId** table_symboles = NULL;
    int compteur_numVarTmp   = -1;
    variable* var            = NULL;
    fonction* fct            = NULL;
    int lvl                  = 1;
    
 // pile pour les identifiants
    char** pile_id            = NULL;
    int cur_index             = 0;

    // utilisé pour la gestion des tableaux
    int type                  = 0;
    int* bornesinf            = NULL;
    int* bornessup            = NULL;
    int dimensions            = 0;
    int last_expr             = -1;
    int is_table              = FALSE;
    int* indexes              = NULL;
    int cur_index_indexes     = 0;

 // utilisé pour la gestion des fonctions
    char* last_FctName        = NULL;
    int nbArgs                = 0;
    // pile pour les arguments
    int* pile_arg             = NULL;
    int cur_index_arg         = 0;

    // utilisé pour la génération d'une affectation
    // pile opérande
    int* pile_operande        = NULL;
    int* pile_type_operande   = NULL;
    int cur_index_operande    = 0;
    // pile opération
    int* pile_operation       = NULL;
    int cur_index_operation   = 0;
    
    // structure pour la génération de code
    code4adresses* premiereOperation = NULL;
    code4adresses* operationCourante = NULL;
    char* labelPremiereOp            = NULL;

	//compteur des instruction POW (pour généré des labels uniques
	int compteurPOW = 1;
	
	//labels pour les operations ifThenElse
	char *** pile_ifThenElse = NULL;
	int indexIfThenElse=0;
	int compteurIF =1;
	
	//dernière expression :
	int derniereExpr =0;
	int typrDerniereExpr =0;

    int i = 0;
    
    //mémoire tableau et chaine 
    int sommetTableau = 0x20000000;
    
    char * data = NULL;
    int indexData = 0;
    int numChaine =1;

%}

%union {
    char*  identifiant;
    char*  string;
    int    integer;
    int    operation;
}


%token <identifiant> IDENT
%token <string>      CTE
%token <integer>     INTEGER
%token <operation>   AFF ADD SUB MUL DIV POW LT LET GT GET EQ DIFF AND OR XOR NOT
%type <identifiant> identlist
%type <operation>   opb opu

%token PROGRAM
%token VAR
%token UNIT
%token BOOL
%token INT
%token ARRAY
%token CROCHET_OUVRANT
%token CROCHET_FERMANT
%token OF
%token RANGE_MARK
//     RANGE_MARK ==> a .. b, interval de a à b
%token POINT_VIRGULE
%token FUNCTION
%token PARENTHESE_OUVRANTE
%token PARENTHESE_FERMANTE
%token DOUBLE_POINT
%token VIRGULE
%token REF
%token IF
%token THEN
%token ELSE
%token WHILE
%token DO
%token RETURN
%token BBEGIN
%token END
%token READ 
%token WRITE

%right AFF
%left ADD SUB
%left MUL DIV
%left NEG


%start program

%%

program:
    PROGRAM IDENT vardecllist fundecllist {
    	labelPremiereOp = "main";
    }
    instr
    { printf( "-> Compilation réussie\n" ); }
  ;

vardecllist:
    /* Vide */
  | varsdecl
  | varsdecl POINT_VIRGULE vardecllist
  ;

varsdecl:
    VAR identlist DOUBLE_POINT typename
    {   
        for( i = 0 ; i < SIZE_TAB1 ; i++ )
            pile_id[i] = NULL;
        cur_index = 0;

        for( i = 0 ; i < SIZE_TAB1 ; i++ )
            pile_arg[i] = 0;
        cur_index_arg = 0; 
    }
  ;

identlist:
    /* règle pour la déclaration de variable => création de la (des) variable(s)
     * ==> ajout du nom de la (des) variable(s) dans la table des symboles.
     */
    IDENT {
        if( ajouterIdentifiant( yylval.identifiant, 0 ) == -1 )
            printf( "Erreur: cet identifiant existe deja. (var, lvl%d - %s)\n",
                    lvl, yylval.identifiant );
    }
  | IDENT {
        if( ajouterIdentifiant( yylval.identifiant, 0 ) == -1 )
            printf( "Erreur: cet identifiant existe deja. (var, lvl%d - %s)\n",
                    lvl, yylval.identifiant );
    }
    VIRGULE identlist
  ;

typename:
    atomictype
    {
        for( i = 0; i < SIZE_TAB1 && pile_id[i] != NULL; i++ )
        {
            var = retrouverVariable(
                    retrouverIdentifiant(
                        table_symboles,
                        pile_id[i]
                    )
                  );
            
            var->numeroVariableTemporaire = compteur_numVarTmp--;
            var->type = type; pile_arg[cur_index_arg++] = type;
            var->dimenssions = 0;
        }
    }
  | arraytype
  ;

atomictype:
    UNIT { type = 1; }
  | BOOL { type = 2; }
  | INT  { type = 3; }
  ;

arraytype:
    ARRAY CROCHET_OUVRANT rangelist CROCHET_FERMANT OF atomictype
    {
        for( i = 0; i < SIZE_TAB1 && pile_id[i] != NULL; i++ )
        {
            var = retrouverVariable(
                    retrouverIdentifiant(
                        table_symboles,
                        pile_id[i]
                    )
                  );
            
            var->numeroVariableTemporaire = compteur_numVarTmp--;
            var->type = type; pile_arg[cur_index_arg++] = type;
 var->dimenssions = dimensions;
            var->bornesInf = bornesinf;
            var->bornesSup = bornessup;
            
            allouerMemoireTableau( var );
        }
        
        dimensions = 0;
    }
  ;

rangelist:
    INTEGER RANGE_MARK INTEGER
    {   
        bornesinf[dimensions] = $1;
        bornessup[dimensions++] = $3;
    }
  | INTEGER RANGE_MARK INTEGER VIRGULE
    {
        bornesinf[dimensions] = $1;
        bornessup[dimensions++] = $3;
    }
    rangelist
  ;

fundecllist:
    /* Vide */
  | fundecl POINT_VIRGULE fundecllist
  ;

fundecl:
    /* règle pour la déclaration de fonction => création de la (des) fonction(s)
     * ==> ajout du nom de la (des) fonction(s) dans la table des symboles.
     */
    FUNCTION
    IDENT {
        if( ajouterIdentifiant( yylval.identifiant, 1 ) == 0 )
            last_FctName = strdup( yylval.identifiant );
        else
            printf( "Erreur: cet identifiant existe deja. (fct, lvl%d - %s)\n",
                    lvl, yylval.identifiant );
        
        // on entre dans la fonction donc on descend d'un niveau dans la table
        // des symboles
        lvl++;
    }
    PARENTHESE_OUVRANTE arglist PARENTHESE_FERMANTE
    DOUBLE_POINT atomictype
    {
        fct = retrouverFonction(
                retrouverIdentifiant(
                    table_symboles,
                    last_FctName
                )
              );
            
        fct->label = last_FctName;
        fct->retour = type;
        fct->nbArgs = nbArgs; nbArgs = 0;
        fct->typeArgs = (enum typeVariable*)
                        (calloc( 1, (cur_index_arg + 1) * sizeof(int) ));
        memcpy( fct->typeArgs, pile_arg, (cur_index_arg + 1) );

        for( i = 0 ; i < SIZE_TAB1 ; i++ )
            pile_arg[i] = 0;
        cur_index_arg = 0;
    }
    vardecllist instr
    {
        // NOTE debug
        afficherTable(table_symboles);
        
        // on sort de la fonction donc on remonte d'un niveau dans la table
        // des symboles => suppression du niveau de la fonction
        supprimerNiveauTable( table_symboles, lvl );
        lvl--;
    }
  ;

arglist:
    /* Vide */
  | arg
  | arg VIRGULE arglist
  ;

arg:
    IDENT {
        if( ajouterIdentifiant( yylval.identifiant, 0 ) == -1 )
            printf( "Erreur: cet identifiant existe deja. (arg, lvl%d - %s)\n",
                    lvl, yylval.identifiant );
    }
    DOUBLE_POINT typename
    {
        for( i = 0 ; i < SIZE_TAB1 ; i++ )
            pile_id[i] = NULL;
        cur_index = 0;
        
        nbArgs++;
    }
  | REF
    IDENT {
        if( ajouterIdentifiant( yylval.identifiant, 0 ) == -1 )
            printf( "Erreur: cet identifiant existe deja. (arg, lvl%d - %s)\n",
                    lvl, yylval.identifiant );
    }
    DOUBLE_POINT typename
    {   
        for( i = 0 ; i < SIZE_TAB1 ; i++ )
            pile_id[i] = NULL;
        cur_index = 0;
        
        nbArgs++;
    }
  ;

instr:
    IF expr {
    	//generation labels pour if Then else
		char * labelifelse = (char *) calloc(16, sizeof(char));
		char * labeliffin = (char *) calloc(16, sizeof(char));
		snprintf(labelifelse, 16, "if%xe", compteurIF);
		snprintf(labeliffin, 16, "if%xf", compteurIF++);
		pile_ifThenElse[indexIfThenElse] = calloc(2, sizeof(char *));
		pile_ifThenElse[indexIfThenElse][0]=labelifelse;
		pile_ifThenElse[indexIfThenElse++][1]=labeliffin;
		if(typrDerniereExpr == 0){
			genCode(NULL,li,a0,derniereExpr,0,1,typrDerniereExpr,1);
			typrDerniereExpr=1;
			derniereExpr=a0;
		}
		genCode(labeliffin,beqz,derniereExpr, 0,0,typrDerniereExpr,1,1); 
    }
    THEN instr {
    	//ajout du label de fin
    	//genCode(pile_ifThenElse[--indexIfThenElse][1], label, r0,r0,r0,1,1,1);
    	labelPremiereOp = pile_ifThenElse[--indexIfThenElse][1];
    	free(pile_ifThenElse[indexIfThenElse]);
    }
  | IF expr {
    	//generation labels pour if Then else
		char * labelifelse = (char *) calloc(16, sizeof(char));
		char * labeliffin = (char *) calloc(16, sizeof(char));
		snprintf(labelifelse, 16, "if%xe", compteurIF);
		snprintf(labeliffin, 16, "if%xf", compteurIF++);
		pile_ifThenElse[indexIfThenElse] = calloc(2, sizeof(char *));
		pile_ifThenElse[indexIfThenElse][0]=labelifelse;
		pile_ifThenElse[indexIfThenElse++][1]=labeliffin;
		
		if(typrDerniereExpr == 0){
			genCode(NULL,li,a0,derniereExpr,0,1,typrDerniereExpr,1);
			typrDerniereExpr=1;
			derniereExpr=a0;
		}
		genCode(labelifelse,beqz,derniereExpr, 0,0,typrDerniereExpr,1,1); 
    } 
  THEN instr{
  		//saut à la fin et label else
  		genCode(pile_ifThenElse[indexIfThenElse-1][1],j,v0, 0,0,1,1,1);
  		labelPremiereOp = pile_ifThenElse[indexIfThenElse-1][0];
  } ELSE instr{
  		//ajout du label de fin et dépiler
  		//genCode(pile_ifThenElse[--indexIfThenElse][1], label, r0,r0,r0,1,1,1);
    	labelPremiereOp = pile_ifThenElse[--indexIfThenElse][1];
    	free(pile_ifThenElse[indexIfThenElse]);
  }
  | WHILE expr DO instr
  | lvalue AFF {
        pile_operation[cur_index_operation++] = AFF;
    }
    expr
    {

        genAff();

        for( i = 0 ; i < SIZE_TAB2 ; i++ )
        {
            pile_operande[i] = 0;
            pile_type_operande[i] = 0;
            pile_operation[i] = 0;
        }
        cur_index_operande  = 0;
        cur_index_operation = 0;
    }
  | RETURN expr
  | RETURN
  | IDENT PARENTHESE_OUVRANTE exprlist PARENTHESE_FERMANTE
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !fonctionExisteAuNiveau( p_Id, lvl ) )
                printf( "Erreur: fonction inexistante (%s)\n", $1 );
        }
        else
            printf( "Erreur: fonction inexistante (%s)\n", $1 );
    }
  | IDENT PARENTHESE_OUVRANTE PARENTHESE_FERMANTE
    {
        pileIdentifiants* p_Id; 
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !fonctionExisteAuNiveau( p_Id, lvl ) )
                printf( "Erreur: fonction inexistante (%s)\n", $1 );
        }
        else
            printf( "Erreur: fonction inexistante (%s)\n", $1 );
    }
  | BBEGIN sequence END
  | BBEGIN END
  | READ lvalue
  | WRITE lvalue{
  		int adresseVariable = a0;
		genCode(NULL, move, a0, pile_operande[cur_index_operande-1],r0, 1, 1,1);
  		cur_index_operande--;
  		genCode(NULL, li, v0, 1,r0, 1, 0,0);
  		genCode(NULL, syscall, 0, 0,0, 1, 1,1);
  }
  | WRITE INTEGER {
  		int adresseVariable = a0;
		genCode(NULL, li, a0, $2,r0, 1, 0,1);
  		genCode(NULL, li, v0, 1,r0, 1, 0,0);
  		genCode(NULL, syscall, 0, 0,0, 1, 1,1);
  }
  | WRITE CTE{
  		int taille = strlen(yylval.string)+1;
  		char * dataLabel = (char *) calloc(16, sizeof(char));
  		snprintf(dataLabel, 16, "str%d", numChaine++);
  		sprintf(data, "%s%s:\t.asciiz %s\n", data, dataLabel, yylval.string);
  		
  		//strcat(data, dataLabel);
  		//strcat(data, "\t.asciiz \"");
  		//strcat(data, yylval.string);
  		//strcat(data, "\"");
  		
  		genCode(dataLabel, liChaine, a0, r0, r0,1,1,1);
  		genCode(NULL, li, v0, 4, r0,1,0,1);
  		genCode(NULL, syscall, r0, r0, r0,1,1,1);
  }
  ;

sequence:
    instr POINT_VIRGULE sequence
  | instr POINT_VIRGULE
  | instr
  ;

lvalue:
    IDENT
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !variableExisteAuNiveau( p_Id, lvl ) )
            {
                printf( "Erreur: variable inexistante (%s)\n", $1 );
            }
            else
            {
                var = retrouverVariable( p_Id );
                pile_operande[cur_index_operande] =
                                                  var->numeroVariableTemporaire;
                pile_type_operande[cur_index_operande++] = ADDR_MEM;
            }
        }
        else
            printf( "Erreur: variable inexistante (%s)\n", $1 );
    }
 | IDENT CROCHET_OUVRANT
    {
        is_table  = TRUE;
        last_expr = INT;
    }
    exprlist CROCHET_FERMANT
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !variableExisteAuNiveau( p_Id, lvl ) )
            {
                printf( "Erreur: tableau inexistante (%s)\n", $1 );
            }
            else
            {
                if( last_expr == INT )
                // alors c'est un accès statique au tableau
                {
                    var = retrouverVariable( p_Id );
                    pile_operande[cur_index_operande] =
                            calculerAdresseCaseTableauStatic( var,
                                                              var->dimenssions,
                                                              indexes );
                    pile_type_operande[cur_index_operande++] = ADDR_MEM;
                }
                
                is_table = FALSE;
                cur_index_indexes = 0;
            }
        }
        else
            printf( "Erreur: tableau inexistante (%s)\n", $1 );
    }
  ;

exprlist:
    expr
  | expr VIRGULE exprlist
  ;

expr:
    INTEGER
    {
        if( is_table == TRUE )
        {
            indexes[cur_index_indexes++] = $1;
        }
        else
        {
            pile_operande[cur_index_operande] = $1;
            pile_type_operande[cur_index_operande++] = CONSTANTE;
        }
      derniereExpr = $1;
        typrDerniereExpr = 0;
    }
  | PARENTHESE_OUVRANTE
  {	pile_operation[cur_index_operation++] = PARENTHESE_OUVRANTE;}
   expr PARENTHESE_FERMANTE
   {	pile_operation[cur_index_operation++] = PARENTHESE_FERMANTE;}
  | expr opb {
        pile_operation[cur_index_operation++] = yylval.operation;
    }
    expr
  | opu {
        pile_operation[cur_index_operation++] = yylval.operation;
    }
    expr
  | IDENT PARENTHESE_OUVRANTE exprlist PARENTHESE_FERMANTE
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
           if( !fonctionExisteAuNiveau( p_Id, lvl ) )
            {
                printf( "Erreur: fonction inexistante (%s)\n", $1 );
            }
            else
            {
                last_expr = _ID;
            }
        }
        else
            printf( "Erreur: fonction inexistante (%s)\n", $1 );
    }
  | IDENT PARENTHESE_OUVRANTE PARENTHESE_FERMANTE
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !fonctionExisteAuNiveau( p_Id, lvl ) )
            {
                printf( "Erreur: fonction inexistante (%s)\n", $1 );
            }
            else
            {
                last_expr = _ID;
            }
        }
        else
            printf( "Erreur: fonction inexistante (%s)\n", $1 );
    }
  | IDENT CROCHET_OUVRANT
    {
        is_table  = TRUE;
        last_expr = INT;
    }
    exprlist CROCHET_FERMANT
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !variableExisteAuNiveau( p_Id, lvl ) )
            {
                printf( "Erreur: tableau inexistante (%s)\n", $1 );
            }
            else
            {
                if( last_expr == INT )
                // alors c'est un accès statique au tableau
                {
                    var = retrouverVariable( p_Id );
                    pile_operande[cur_index_operande] =
                            calculerAdresseCaseTableauStatic( var,
                                                              var->dimenssions,
                                                              indexes );
                    pile_type_operande[cur_index_operande++] = ADDR_MEM;
                }
                
                is_table = FALSE;
                cur_index_indexes = 0;
            }
        }
        else
            printf( "Erreur: tableau inexistante (%s)\n", $1 );
    }
  | IDENT
    {
        pileIdentifiants* p_Id;
        if( (p_Id = retrouverIdentifiant( table_symboles, $1 )) != NULL )
        {
            if( !variableExisteAuNiveau( p_Id, lvl ) )
            {
                printf( "Erreur: variable inexistante (%s)\n", $1 );
            }
            else
            {
                last_expr = _ID;
                
                var = retrouverVariable( p_Id );
                pile_operande[cur_index_operande] =
                                                  var->numeroVariableTemporaire;
                pile_type_operande[cur_index_operande++] = ADDR_MEM;
            }
        }
        else
            printf( "Erreur: variable inexistante (%s)\n", $1 );
    }
  ;

opb:
    ADD | SUB | MUL | DIV | POW | LT | LET | GT | GET | EQ | DIFF
  | AND | OR  | XOR
  ;

opu:
    SUB %prec NEG | NOT
  ;


%%

/*
 * Ajouter un nouvel identifiant a la table des symboles
 * - str: l'identifiant
 * - type: 0 pour une variable, 1 pour une fonction
 * retourne 0 en cas de succès, -1 sinon
 */
int ajouterIdentifiant( char* str, int type )
{
    // recherche de l'existence de l'identifiant donné en paramètre
    // création de celui-ci si non trouvé dans la table des symboles
    pileIdentifiants* p_Id = NULL;
    if( (p_Id = retrouverIdentifiant( table_symboles, str )) == NULL )
        p_Id = nouvelIdentifiant( table_symboles, str );


    if( type == VARIABLE )
    {
        if( variableExisteAuNiveau( p_Id, lvl ) )
            return -1;
        
        // création et assignation de la représentation de la variable dans
        // la table des symboles.
        var = (variable*)(calloc( 1, sizeof(variable) ));
        ajoutVariable( p_Id, var, lvl );

        pile_id[cur_index++] = str;
    }
    else if( type == FONCTION )
    {
        if( fonctionExisteAuNiveau( p_Id, lvl ) )
            return -1;
        
        // création et assignation de la représentation de la fonction dans
        // la table des symboles.
        fonction* fct = (fonction*)(calloc( 1, sizeof(fonction) ));
        ajoutFonction( p_Id, fct, lvl );
    }


    return 0;
}

int yyerror( const char* str )
{
    fprintf( stderr, "Error: %s\n", str );
}

int main( int argc, char* argv[] )
{
    int i = 0;

    pile_id = (char**)(calloc( SIZE_TAB1, sizeof(char*) ));
    pile_arg = (int*)(calloc( SIZE_TAB1, sizeof(int) ));
    pile_operande = (int*)(calloc( SIZE_TAB2, sizeof(int) ));
    pile_type_operande = (int*)(calloc( SIZE_TAB2, sizeof(int) ));
    pile_operation = (int*)(calloc( SIZE_TAB2, sizeof(int) ));

    bornesinf = (int*)(calloc( SIZE_TAB2, sizeof(int) ));
    bornessup = (int*)(calloc( SIZE_TAB2, sizeof(int) ));
    indexes = (int*)(calloc( SIZE_TAB2, sizeof(int) ));


    // allocation mémoire pour la table des symboles
    // ( <=> tableau de listeId <=> table de hashage )
    // et initialisation du contenu de la table
    table_symboles = (listeId**)(calloc( N, sizeof(listeId*) ));


	//allocation mémoire pile ifThenElse
	pile_ifThenElse = (char ***) calloc(128 , sizeof(char **));
	
	data = (char *)calloc(1024, sizeof(char));
	snprintf(data, 1024, "\t.data\n"); 
	indexData = strlen("\t.data\n");
	

    // parsing du fichier en entrée
    yyin = fopen( argv[1], "r" );
    yyparse();
    fclose(yyin);


    // génération de code intermédiaire (code3adresses)
    toutSpiller( premiereOperation );
    while( premiereOperation != NULL && premiereOperation->precedent != NULL )
        premiereOperation = premiereOperation->precedent;
    ecritureDansUnFichier( "./out.scalpa", premiereOperation );


    // Libération de la mémoire utilisée par la table des symboles
    for( i = 0 ; i < N ; i++ )
        if( table_symboles[i] != NULL )
            free(table_symboles[i]);
    free(table_symboles);


    return EXIT_SUCCESS;
}


